`include "./Add_4.v"
`include "./AddImm.v"
`include "./ALU.v"
`include "./ALUcontrol.v"
`include "./Control.v"
`include "./ImmGen.v"
`include "./Memory.v"
`include "./Mux.v"
`include "./registers.v"
`include "./ShiftLeft_1.v"

`timescale 10ps/1ps

module simpleCPU(clk, reset);
    input wire clk, reset;

    //PC/指令存储器
    reg[31:0] PC;
    wire[31:0] instruction;

    Memory InstructionMemory(clk, reset, PC, 32'h00000000, 1'b1, 1'b0, instruction);

    //CU
    wire Branch, MemRead, MemtoReg, MemWrite, ALUSrc, RegWrite;
    wire[1:0] ALUop;
    Control CU(clk, reset, instruction[6:0],
            Branch, MemRead, MemtoReg, ALUop, MemWrite, ALUSrc, RegWrite);

    //regs
    wire[31:0] readData1, readData2;
    registers regs(clk, reset, 
                    instruction[19:15], instruction[24:20], instruction[11:7], MEMMUXOUT, 
                    readData1, readData2, RegWrite);

    //ImmGen
    wire[63:0] extendedImm;
    ImmGen immGen(instruction, extendedImm);

    //ALUCTRL
    wire[3:0] ALUCTRLOUT;
    ALUcontrol ALUctrl(clk, reset, ALUop, instruction[31:25], instruction[14:12], ALUCTRLOUT);

    //ALU MUX
    wire[31:0] ALUMUXOUT_data2;
    Mux ALUMUX(readData2, extendedImm[31:0], ALUSrc, ALUMUXOUT_data2);

    //ALU
    wire[31:0] ALUresult;
    wire ALUZeroResult;

    ALU U0(clk, reset, ALUCTRLOUT, readData1, ALUMUXOUT_data2, ALUZeroResult, ALUresult);

    //DATA MEM
    wire[31:0] memReadData;
    Memory dataMemory(clk, reset, ALUresult, readData2, MemRead, MemWrite, memReadData);

    //MEM MUX
    wire[31:0] MEMMUXOUT;
    Mux MEMMUX(ALUresult, memReadData, MemtoReg, MEMMUXOUT);

    //branch and pc + 4
    //shift left 1
    wire[63:0] shiftedImm;
    ShiftLeft_1 shiftLeft_1(extendedImm, shiftedImm);

    //add pc
    wire[31:0] pcAddImm;
    AddImm addImm(clk, reset, PC, shiftedImm, pcAddImm);

    //pc + 4
    wire[31:0] pcAdd4;
    Add_4 add_4(PC, pcAdd4);

    //branch mux
    wire[31:0] newPC;
    wire branchMuxCtrl = Branch & ALUZeroResult;
    Mux branchMux(pcAdd4, pcAddImm, branchMuxCtrl, newPC);

    always @(posedge clk, posedge reset)
    begin
        if(reset)
            PC <= 0;
        else
            PC <= newPC;
    end

endmodule